package org.fixies.spreadsheet.excel.jxl;

import jxl.Cell;
import jxl.Sheet;
import org.fixies.spreadsheet.excel.ExcelSheetContext;
import org.fixies.utils.UnexpectedProblemException;
import org.fixies.utils.Validator;

import java.util.ArrayList;
import java.util.List;

/**
 * Java Excel APIを用いたSheetContextインタフェース実装クラス
 *
 * @author Kawasaki
 */
public class JxlSheetContext extends ExcelSheetContext {

    /**
     * シート
     */
    private final Sheet sheet_;

    /**
     * 現在処理中の行数
     */
    private int currentRowNumber_ = -1;

    /**
     * 最終行
     */
    private final int lastIndexOfRow_;

    /**
     * コンストラクタ
     *
     * @param sheet シート
     */
    public JxlSheetContext(Sheet sheet) {
        sheet_ = sheet;

        lastIndexOfRow_ = sheet_.getRows() - 1;
        currentRowNumber_ = getNextRowNumber();
    }

    public String[] current() {
        Cell[] cells = sheet_.getRow(currentRowNumber_);
        return convertToStrings(cells);
    }

    /**
     * セル群を文字列に変換する。
     *
     * @param cells セル
     * @return 文字列
     */
    private String[] convertToStrings(Cell[] cells) {
        if (cells == null) {
            return null;
        }
        List<String> strs = new ArrayList<String>(cells.length);
        for (Cell cell : cells) {
            if (isCommentCell(cell.getContents())) {
                break;
            }
            strs.add(convertToString(cell));
        }
        return strs.toArray(new String[strs.size()]);
    }

    /**
     * セルを文字列に変換する。
     *
     * @param cell セル
     * @return 文字列
     */
    private String convertToString(Cell cell) {
        return cell.getContents();
    }

    /**
     * @see org.fixies.spreadsheet.interpreter.SheetContext#getSheetName()
     */
    public String getSheetName() {
        return sheet_.getName();
    }

    /**
     * @see org.fixies.spreadsheet.interpreter.SheetContext#next()
     */
    public String[] next() {

        try {
            currentRowNumber_ = getNextRowNumber();
        } catch (RuntimeException e) {
            throw new UnexpectedProblemException(e,
                    "Read error. sheet=[", sheet_.getName(), "] row=[", (currentRowNumber_ + 1), "]");
        }
        if (currentRowNumber_ == END_OF_CONTEXT) {
            return null;
        }
        return current();
    }

    /**
     * 次の行番号を取得する。
     * 有効でない行はスキップされる。 有効でない行とは、コメント行、非表示行を指す。
     *
     * @return 次の行番号
     */
    protected final int getNextRowNumber() {

        if (currentRowNumber_ == END_OF_CONTEXT) {
            return END_OF_CONTEXT;
        }

        for (int searchIdx = currentRowNumber_ + 1; searchIdx <= lastIndexOfRow_; searchIdx++) {
            if (isValidRow(searchIdx)) {
                return searchIdx;
            }
        }
        return END_OF_CONTEXT;
    }

    /**
     * 有効な行であるか判定する。川崎健
     * コメント行、または非表示行の場合は、無効な行と判定される。
     *
     * @param rowIndex 判定対象となる行
     * @return 判定結果
     */
    private boolean isValidRow(int rowIndex) {
        Cell[] cells = sheet_.getRow(rowIndex);
        if (Validator.isNullOrEmpty(cells)) return true;
        return !cells[0].isHidden() && !isCommentRow(cells);

    }

    /**
     * コメント行であるか判定する。
     *
     * @param row 行データ
     * @return 判定結果
     */
    private boolean isCommentRow(Cell[] row) {
        return row.length != 0 && isCommentCell(row[0].getContents());
    }
}
